//
//  server.c
//  Batalla Naval
//
//  Created by Federico Raimondo on 4/24/13.
//  Copyright (c) 2013 ar.dc.uba.so. All rights reserved.
//

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <modelo.h>
#include <decodificador.h>
#include <globales.h>
#include <pthread.h>


#define MAX_MSG_LENGTH 4096
#define MAX_JUGADORES 100

void* atendedor_jugador (void* j);
void* atendedor_controlador (void* j);

/* Setea un socket como no bloqueante */
int no_bloqueante(int fd) {
    int flags;
    /* Toma los flags del fd y agrega O_NONBLOCK */
    if ((flags = fcntl(fd, F_GETFL, 0)) == -1 )
        flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}


/* Variables globales del server */
int sock;                                              // Socket donde se escuchan las conexiones entrantes
int sock2; //socket para controlador
struct sockaddr_in name, remote;        // Direcciones
char buf[MAX_MSG_LENGTH];                       // Buffer de recepción de mensajes
int s[MAX_JUGADORES];                           // Sockets de los jugadores

pthread_t thread[MAX_JUGADORES];

int ids[MAX_JUGADORES];                         // Ids de los jugadores
Modelo * model = NULL;                          // Puntero al modelo del juego
Decodificador *decoder  = NULL;         // Puntero al decodificador
int n, tamanio, tamanio_barcos;         // Variables de configuracion del juego.

bool sale = false; //se supone que tiene que estar aca


/* Resetea el juego */
void reset() {
        if (model != NULL) {
                delete model;
        }
        if (decoder != NULL) {
                delete decoder;
        }
        model = new Modelo(n, tamanio, tamanio_barcos);
        decoder = new Decodificador(model);
}

/* Acepta todas las conexiones entrantes */




/* Socket de comunicación del controlador */
int s_controlador;
/* Para anteder al controlador */
void atender_controlador() {
        int recibido;
        std::string resp;
        recibido = recv(s_controlador, buf, MAX_MSG_LENGTH, 0);
        if (recibido < 0) {
                perror("Recibiendo ");          
        } else if (recibido > 0) {
                buf[recibido]='\0';
                char * pch = strtok(buf, "|");
                while (pch != NULL) {
                        //Ejecutar y responder
                        resp = decoder->decodificar(pch);
                        send(s_controlador,resp.c_str(), resp.length() +1, 0);
                        pch = strtok(NULL, "|");
                }
        }
}


/* Para atender al i-esimo jugador */
void atender_jugador(int i) {
        int recibido;
        std::string resp;
        recibido = recv(s[i], buf, MAX_MSG_LENGTH, 0);
        if (recibido < 0) {
                perror("Recibiendo ");
               
        } else if (recibido > 0) {
                buf[recibido]='\0';
                // Separo los mensajes por el caracter |
                char * pch = strtok(buf, "|");
                while (pch != NULL) {
                       
                        // No muestro por pantalla los NOP, son muchos
                        if (strstr(pch, "Nop") == NULL) {
                                printf("Recibido: %s\n", pch);
                        }
                       
                        //Decodifico el mensaje y obtengo una respuesta
                        resp = decoder->decodificar(pch);
                       
                        // Si no se cual es el ID de este jugador, trato de obtenerlo de la respuesta
                        if (ids[i] == -1) {
                                ids[i] = decoder->dameIdJugador(resp.c_str());
                        }
                       
                        // Envio la respuesta
                        send(s[i],resp.c_str(), resp.length() +1, 0);
                       
                        // No muestro por pantalla los NOP, son muchos
                        if (strstr(pch, "Nop") == NULL) {
                                printf("Resultado %s\n", resp.c_str());
                        }
                       
                        // Si ya se cual es el jugador
                        if (ids[i] != -1) {
                                // Busco si hay eventos para enviar y los mando
                                int eventos = model->hayEventos(ids[i]);
                                if (eventos != 0) {
                                        printf("Agregando %d eventos\n", eventos);
                                }
                                for (int ev = 0; ev < eventos; ev++) {
                                        resp = decoder->encodeEvent(ids[i]);
                                        printf("Enviando evento %s", resp.c_str());
                                        send(s[i],resp.c_str(), resp.length() +1, 0);
                                }
                        }
                        pch = strtok(NULL, "|");
                }
        }
}

void* atendedor_controlador(/*void* j*/){
       
        while (!sale) { // mientras no sale lee si se escribio en el file descriptor del controlador y lo atiendo en atender_controlador
                /*fd_set readfds;
                FD_ZERO(&readfds);

                FD_SET(s_controlador, &readfds);
               
                select(s_controlador+1, &readfds, NULL, NULL, NULL); //innesesario hacer select
               
                if (FD_ISSET(s_controlador, &readfds)) {*/
                        atender_controlador();
                //}
        }

        close(s_controlador);
        return NULL;
}

void* atendedor_jugador(void* j){
        int i = *((int*)j);
        //delete (int*)j;

       
        while (!sale) { // mientras no sale lee si se escribio en el file descriptor del socket i (estoy atendiendo al jugador i) y lo atiendo en atender_jugador
                /*fd_set readfds;
                FD_ZERO(&readfds);

                FD_SET(s[i], &readfds);
               
                select(s[n-1]+1, &readfds, NULL, NULL, NULL);
               
                if (FD_ISSET(s[i], &readfds)) {
			printf("atendiendo jugador %d\n", i);*/
			atender_jugador(i);
                //}
        }

        close(s[i]);
        return NULL;
}


void accept() {
        int t;
	int tid = 0;
        for (int i = 0; i < n; i++) {
                t = sizeof(remote);
                if ((s[i] = accept(sock, (struct sockaddr*) &remote, (socklen_t*) &t)) == -1) {
                        perror("aceptando la conexión entrante");
                        exit(1);
                }else{

		        ids[i] = -1;
		        int flag = 1;
		        setsockopt(s[i],            /* socket affected */
		        IPPROTO_TCP,     /* set option at TCP level */
		        TCP_NODELAY,     /* name of option */
		        (char *) &flag,  /* the cast is historical */
		        sizeof(int));    /* length of option value */

			printf("loopeo %d\n", i);
               
		        //creo un thread por jugador
		        pthread_create(&thread[tid], NULL, &atendedor_jugador, &i);
			tid++;
		}
        }
       
}

void acceptcontrolador(){
        int t;
        t = sizeof(remote);
        if ((s_controlador = accept(sock2, (struct sockaddr*) &remote, (socklen_t*) &t)) == -1) {
                perror("aceptando la conexión entrante de controlador");
                exit(1);
        }
        int flag = 1;
        setsockopt(s_controlador,            /* socket affected */
        IPPROTO_TCP,     /* set option at TCP level */
        TCP_NODELAY,     /* name of option */
        (char *) &flag,  /* the cast is historical */
        sizeof(int));    /* length of option value */
       
        //creo un thread para el controlador
        /*pthread_t tid;
        pthread_create(&tid, NULL, &*/atendedor_controlador();//, &tid);
 
}

/*
 * Recibe 4 parametros:
 * argv[1]: Puerto
 * argv[2]: Cantidad de jugadores (N)
 * argv[3]: Tamanio del tablero
 * argv[4]: Tamanio total de los barcos
 */
int main(int argc, char * argv[]) {
        if (argc < 5) {
                printf("Faltan parametros\n");
                printf("Se espera ./server puerto jugadores tamanio_tablero tamanio_barcos\n");
                exit(1);
        }
        int port = atoi(argv[1]);
        n = atoi(argv[2]);
        tamanio = atoi(argv[3]);
        tamanio_barcos = atoi(argv[4]);
       
        inicializar();
        int port_controlador = CONTROLLER_PORT;
       
       
        printf("Escuchando en el puerto %d - controlador en %d\n", port, port_controlador);
        printf("Jugadores %d - Tamanio %d - Tamanio Barcos %d\n", n, tamanio, tamanio_barcos);
        reset();

       
       
        /* Crear socket sobre el que se lee: dominio INET, protocolo TCP (STREAM). */
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0) {
                perror("abriendo socket");
                exit(1);
        }
        /* Crear nombre, usamos INADDR_ANY para indicar que cualquiera puede enviar aquí. */
        name.sin_family = AF_INET;
        name.sin_addr.s_addr = INADDR_ANY;
        name.sin_port = htons(port);
        if (bind(sock, (const struct sockaddr*) &name, sizeof(name))) {
                perror("binding socket");
                exit(1);
        }
       
        /* Escuchar en el socket y permitir n conexiones en espera. */
        if (listen(sock, n) == -1) {
                perror("escuchando");
                exit(1);
        }

        accept();
	
	printf("Corriendo...\n");
////////////////////////////////socket controlador:
        sock2 = socket(AF_INET, SOCK_STREAM, 0);
        if (sock2 < 0) {
                perror("abriendo socket2");
                exit(1);
        }
        /* Crear nombre, usamos INADDR_ANY para indicar que cualquiera puede enviar aquí. */
        name.sin_family = AF_INET;
        name.sin_addr.s_addr = INADDR_ANY;
        name.sin_port = htons(CONTROLLER_PORT);
        if (bind(sock2, (const struct sockaddr*) &name, sizeof(name))) {
                perror("binding socket2");
                exit(1);
        }
       
        /* Escuchar en el socket y permitir n conexiones en espera. */
        if (listen(sock2, 1) == -1) {
                perror("escuchando2");
                exit(1);
        }

        acceptcontrolador();
///////////fin controlador.
               
       

        close(sock);
        close(sock2);
        return 0;
}

